Skip to content

Conversation

ashpil
Copy link

@ashpil ashpil commented Oct 8, 2025

The intent of this PR is to allow specifying SPIR-V bindings without hacky inline assembly.

SPIR-V descriptors and locations can now be specified like this:

const out = @extern(*addrspace(.output) Out, .{
    .name = "out",
    .decoration = .{
        .location = 0,
    },
});

That is, using the @extern builtin.

There are a couple unresolved questions:

  1. SPIR-V does not communicate this binding via a string name, and thus, the ExternOptions.name is meaningless and does nothing. It would be nice if specifying this name was optional. Happy to make this change as well, though we'd have to decide what that means for other targets (e.g., is there a check to make sure it is specified when needed, or should it just inherit from the variable name if it is unspecified, like the extern keyword?).
  2. SPIR-V does not actually require @extern values to be pointers, but Zig does. My understanding is that currently we do some sort of magic dereference in the SPIR-V backend. Not sure if it makes sense to relax this requirement in Zig.

But overall, even with those questions unresolved, this already provides a much more pleasant GPU programming experience.

flags: Flags,
owner_nav: Nav.Index,
zir_index: TrackedInst.Index,
location_or_descriptor_set: u32,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is definitely ugly, haven't seen other examples of large tagged unions in the intern pool so I'm not sure what the preferred pattern here is

@Kbz-8
Copy link

Kbz-8 commented Oct 8, 2025

I don't know what it would imply but if we replace std.builtin.AddressSpace by a tagged union we may be able to achieve a syntax like this, which would be easier to read and write imo (or maybe I'm completely wrong)

extern var frag_color: @Vector(4, f32) addrspace(.output = .{ .location = 0 });
extern const ubo: UBO addrspace(.uniform = .{ .set = 0, .binding = 0 });

It would even semantically fix the issue where you can setup a set/binding on an input/output variable like so

const out = @extern(*addrspace(.output) Out, .{
    .name = "out",
    .decoration = .{
        .descriptor = .{ .set = 0, .binding = 0 },
    },
});

@alichraghi
Copy link
Contributor

Address space and descriptor/binding location are completely different concepts. Moving them into address space would result unintended inequality. I think @extern is the perfect spot.

@Kbz-8
Copy link

Kbz-8 commented Oct 8, 2025

Address space and descriptor/binding location are completely different concepts. Moving them into address space would result unintended inequality. I think @extern is the perfect spot.

Conceptually yes, but a core tenet of Zig's design is that readability is favored over writability and this is rough to read in a shader. Weren't there discussions about adding more SPIR-V builtins like @workGroupSize ? @location, @set and @binding would be the best solution.

@mororo250
Copy link

I do agree with @Kbz-8. I believe that adding specific SPIR-V builtin would be beneficial here. Though I think this pr is a great improvement

@Kbz-8
Copy link

Kbz-8 commented Oct 9, 2025

I thinks a discussion should be done where everyone sits and speaks about how Zig shaders should be written instead of waiting for potential PRs.

@ashpil
Copy link
Author

ashpil commented Oct 11, 2025

But this is what @extern is for — bindings to external things. We can also add a wrapper to std.gpu:

pub fn descriptor(T: type, comptime binding: u32, comptime set: u32) T {
    return @extern(T, .{
        .name = "unused",
        .decoration = .{
            .descriptor = .{ .binding = binding, .set = set },
        }
    });
}

const my_image = descriptor(*const Image, 0, 0);

though the unused name is a little annoying.

I think there are very many potential SPIR-V builtins and I don’t know if Zig wants to go down that rabbit hole. We should be reducing the number of builtins imo, not increasing it.

The nice thing about just using @extern is this makes CPU Zig experience transferable to GPU Zig experience. If I’ve only ever read CPU Zig code and I see @extern on the GPU, I get a pretty good idea of what it’s doing, which doesn’t happen if it’s using some new builtin.

(reposing on correct account)

@ashpil
Copy link
Author

ashpil commented Oct 11, 2025

I thinks a discussion should be done where everyone sits and speaks about how Zig shaders should be written instead of waiting for potential PRs.

Feel free to submit a proposal. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants